//
//  BQSpringCollectionViewFlowLayout.m
//  BQSpringViewController
//
//  Created by Zhiqiang Bao on 14-12-3.
//  Copyright (c) 2014年 Zhiqiang Bao. All rights reserved.
//

#import "BQSpringCollectionViewLayout.h"

#define kPreloadRegionLength        100.f

#define kDefaultItemWidth           320.f
#define kDefaultItemHeight          44.f

#define kDefaultInset               UIEdgeInsetsZero

#define kDefaultScrollDriction      UICollectionViewScrollDirectionVertical

#define kDefaultMinLineSpaceing     1.f
#define kDefaultMinRowSpaceing      0

#define kDefaultSpringStrongth      1.f

@interface BQSpringCollectionViewFlowLayout ()

@property (nonatomic, strong) UIDynamicAnimator *dynamicAnimator;
@property (nonatomic, assign) CGPoint           latestDelta;

@end

@implementation BQSpringCollectionViewFlowLayout

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.minimumInteritemSpacing = kDefaultMinRowSpaceing;
        self.minimumLineSpacing = kDefaultMinLineSpaceing;
        self.itemSize = CGSizeMake(kDefaultItemWidth, kDefaultItemHeight);
        self.sectionInset = kDefaultInset;
        self.scrollDirection = kDefaultScrollDriction;
        self.dynamicAnimator = [[UIDynamicAnimator alloc] initWithCollectionViewLayout:self];
        self.latestDelta = CGPointZero;
        self.springStrongth = kDefaultSpringStrongth;
    }
    return self;
}

- (void)prepareLayout
{
    // call the super prepareLayout first
    [super prepareLayout];
    
    // get the visible cells
    CGRect visibleRect = CGRectMake(self.collectionView.bounds.origin.x - kPreloadRegionLength,
                                    self.collectionView.bounds.origin.y - kPreloadRegionLength,
                                    self.collectionView.frame.size.width + kPreloadRegionLength + kPreloadRegionLength,
                                    self.collectionView.frame.size.height + kPreloadRegionLength + kPreloadRegionLength);
    
    // transform to NSSet
    NSArray *visibleCells = [super layoutAttributesForElementsInRect:visibleRect];
    NSArray *visibleIndexPaths = [visibleCells valueForKey:@"indexPath"];
    NSSet *itemsIndexPathsInVisibleCells = [NSSet setWithArray:visibleIndexPaths];
    NSMutableSet *currentBehaviorsCells = [NSMutableSet set];
    
    // find the behaviors to remove
    NSMutableArray *behaviorsToRemove = [NSMutableArray array];
    [self.dynamicAnimator.behaviors enumerateObjectsUsingBlock:^(UIAttachmentBehavior *obj, NSUInteger index, BOOL *stop) {
        if ([obj isKindOfClass:[UIAttachmentBehavior class]]) {
            if (![itemsIndexPathsInVisibleCells containsObject:[obj.items.firstObject indexPath]]) {
                [behaviorsToRemove addObject:obj];
            }
            [currentBehaviorsCells addObject:[obj.items.firstObject indexPath]];
        }
    }];
    
    // find the behaviors to add
    NSMutableArray *behaviorsToAdd = [NSMutableArray array];
    [visibleCells enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger index, BOOL *stop) {
        if ([obj isKindOfClass:[UICollectionViewLayoutAttributes class]]) {
            if (![currentBehaviorsCells containsObject:[obj indexPath]]) {
                [behaviorsToAdd addObject:obj];
            }
        }
    }];
        
    // remove behaviors
    [behaviorsToRemove enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [self.dynamicAnimator removeBehavior:obj];
    }];
    
    // get the touch location
    CGPoint touchLocation = [self.collectionView.panGestureRecognizer locationInView:self.collectionView];
    
    // add behaviors
    [behaviorsToAdd enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL *stop) {
        CGPoint center = obj.center;
        UIAttachmentBehavior *springBehaviour = [[UIAttachmentBehavior alloc] initWithItem:obj attachedToAnchor:center];
        
        springBehaviour.length = 0.0f;
        springBehaviour.damping = 0.8f;
        springBehaviour.frequency = 1.0f;
        
        // If our touchLocation is not (0,0), we'll need to adjust our item's center "in flight"
        if (!CGPointEqualToPoint(CGPointZero, touchLocation)) {
            CGFloat deltaX = self.latestDelta.x;
            CGFloat deltaY = self.latestDelta.y;
            
            CGFloat yDistanceFromTouch = fabsf(touchLocation.y - springBehaviour.anchorPoint.y);
            CGFloat xDistanceFromTouch = fabsf(touchLocation.x - springBehaviour.anchorPoint.x);
            
            CGFloat scrollRateX = xDistanceFromTouch / self.collectionView.frame.size.width;
            CGFloat scrollRateY = yDistanceFromTouch / self.collectionView.frame.size.height;
            
            center.x += deltaX < 0 ? MAX(deltaX, deltaX * scrollRateX) : MIN(deltaX, deltaX * scrollRateX);
            center.y += deltaY < 0 ? MAX(deltaY, deltaY * scrollRateY) : MIN(deltaY, deltaY * scrollRateY);
            
            obj.center = center;
        }
        
        [self.dynamicAnimator addBehavior:springBehaviour];
    }];
    
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return [self.dynamicAnimator itemsInRect:rect];
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.dynamicAnimator layoutAttributesForCellAtIndexPath:indexPath];
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    UIScrollView *scrollView = self.collectionView;
    CGFloat deltaX = newBounds.origin.x - scrollView.bounds.origin.x;
    CGFloat deltaY = newBounds.origin.y - scrollView.bounds.origin.y;
    
    self.latestDelta = CGPointMake(deltaX, deltaY);
    
    CGPoint touchLocation = [self.collectionView.panGestureRecognizer locationInView:self.collectionView];
    
    [self.dynamicAnimator.behaviors enumerateObjectsUsingBlock:^(UIAttachmentBehavior *springBehaviour, NSUInteger idx, BOOL *stop) {
        CGFloat yDistanceFromTouch = fabsf(touchLocation.y - springBehaviour.anchorPoint.y);
        CGFloat xDistanceFromTouch = fabsf(touchLocation.x - springBehaviour.anchorPoint.x);
        CGFloat scrollRateX = xDistanceFromTouch / self.collectionView.frame.size.width * _springStrongth;
        CGFloat scrollRateY = yDistanceFromTouch / self.collectionView.frame.size.height * _springStrongth;
        
        UICollectionViewLayoutAttributes *item = [springBehaviour.items firstObject];
        CGPoint center = item.center;
        
        center.x += deltaX < 0 ? MAX(deltaX, deltaX * scrollRateX) : MIN(deltaX, deltaX * scrollRateX);
        center.y += deltaY < 0 ? MAX(deltaY, deltaY * scrollRateY) : MIN(deltaY, deltaY * scrollRateY);
        
        item.center = center;
        [self.dynamicAnimator updateItemUsingCurrentState:item];
    }];
    
    return NO;
}

@end
